﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MadLibrary.Collections;
namespace MadLibrary.Sql
{
    /// <summary>
    /// Holds data of specified type and manages it by classic SQL commands.
    /// </summary>
    /// <typeparam name="T">Specified type of data in holder.</typeparam>
    public class SqlData<T> : Collection<T> where T : class, new()
    {
        /// <summary>
        /// Provides filtering of current holder by specified filter.
        /// </summary>
        /// <param name="Filter">The filter.</param>
        /// <returns>Data filtered by specified filter.</returns>
        public SqlData<T> Where(Func<T, bool> Filter)
        {
            SqlData<T> ret = new SqlData<T>();
            foreach(T t in this)
            {
                if(Filter(t))
                    ret.Add(t);
            }
            return ret;
        }
        /// <summary>
        /// Selects objects of specified type from current data holder.
        /// </summary>
        /// <typeparam name="TReturn">Specifies type of object which has to be returned.</typeparam>
        /// <param name="Selection">Specifies selecting method.</param>
        /// <returns>Objects of specified type selected by specified selecting method.</returns>
        public SqlData<TReturn> Select<TReturn>(Func<T, TReturn> Selection) where TReturn : class, new()
        {
            SqlData<TReturn> ret = new SqlData<TReturn>();
            foreach (T t in this)
            {
                ret.Add(Selection(t));
            }
            return ret;
        }
        /// <summary>
        /// Selects more objects from every enumerated object of current instance.
        /// </summary>
        /// <typeparam name="TReturn">The type of the return type.</typeparam>
        /// <param name="Selection">Specifies selecting method.</param>
        /// <returns>More objects of specified type selected by specified selecting method from every enumerated object of current holder.</returns>
        public SqlData<TReturn> SelectMore<TReturn>(Func<T, TReturn[]> Selection) where TReturn : class, new()
        {
            SqlData<TReturn> ret = new SqlData<TReturn>();
            foreach (T t in this)
            {
                TReturn[] rt = Selection(t);
                foreach (TReturn r in rt)
                {
                    ret.Add(r);
                }
            }
            return ret;
        }
        /// <summary>
        /// Selects more objects from every enumerated object of current instance.
        /// </summary>
        /// <typeparam name="TReturn">The type of the return type.</typeparam>
        /// <param name="Selection">Specifies selecting method.</param>
        /// <returns>More objects of specified type selected by specified selecting method from every enumerated object of current holder.</returns>
        public SqlData<TReturn> SelectMore<TReturn>(Func<T, SqlData<TReturn>> Selection) where TReturn : class, new()
        {
            SqlData<TReturn> ret = new SqlData<TReturn>();
            foreach (T t in this)
            {
                SqlData<TReturn> rt = Selection(t);
                foreach (TReturn r in rt)
                {
                    ret.Add(r);
                }
            }
            return ret;
        }
        /// <summary>
        /// Inserts single item to data holder which has to be returned.
        /// </summary>
        /// <param name="item">Specifies item which has to be inserted.</param>
        /// <returns>Data holder with inserted item and original data.</returns>
        public SqlData<T> Insert(T item)
        {
            SqlData<T> ret = new SqlData<T>();
            ret.Add(item);
            return ret;
        }
        /// <summary>
        /// Inserts more items to data holder which has to be returned.
        /// </summary>
        /// <param name="items">Specifies items which has to be inserted.</param>
        /// <returns>Data holder with inserted items and original data.</returns>
        public SqlData<T> Insert(params T[] items)
        {
            SqlData<T> ret = new SqlData<T>();
            foreach (T item in items)
            {
                ret.Add(item);
            }
            return ret;
        }
        /// <summary>
        /// Applicates specified method on each item in current data holder.
        /// </summary>
        /// <param name="Application">Specifies method which has to be applicated on all items in current data holder.</param>
        /// <returns>New data holder with items on which was application successfully done.</returns>
        public SqlData<T> Applicate(Func<T, T> Application)
        {
            SqlData<T> ret = new SqlData<T>();
            foreach (T t in this)
            { 
                ret.Add(Application(t));
            }
            return ret;
        }
        /// <summary>
        /// Deletes items which meets specified comparasion method.
        /// </summary>
        /// <param name="Comparasion">Specifies comparasion which test if passed item meets programmed requirements.</param>
        /// <returns>New data holder without deleted items.</returns>
        public SqlData<T> Delete(Func<T, bool> Comparasion)
        {
            SqlData<T> ret = new SqlData<T>();
            ret = this;
            int i = 0;
            List<int> rm = new List<int>();
            foreach (T t in ret)
            {
                if (Comparasion(t))
                    rm.Add(i);
                i++;
            }
            foreach (int pos in rm)
            {
                ret.RemoveAt(pos);
            }
            return ret;
        }
        /// <summary>
        /// Updates each item specified in current data holder.
        /// </summary>
        /// <param name="Updater">Specifies method which has to update all items in current data holder.</param>
        /// <returns>New data holder with updated items.</returns>
        public SqlData<T> Update(Func<T, T> Updater)
        { 
            SqlData<T> ret = new SqlData<T>();
            foreach (T t in this)
            {
                ret.Add(Updater(t));
            }
            return ret;
        }
        /// <summary>
        /// Gets unique items from current data holder.
        /// </summary>
        /// <returns>Data holder of unique items from current instance.</returns>
        public SqlData<T> Unique()
        {
            SqlData<T> ret = new SqlData<T>();
            foreach(T t in this)
            {
                if (!ret.Contains(t))
                    ret.Add(t);
            }
            return ret;
        }
        /// <summary>
        /// Gets unique items from current data holder.
        /// </summary>
        /// <param name="Comparer">Specifies method which has to compare all items in current data holder.</param>
        /// <returns>Data holder of unique items from current instance.</returns>
        public SqlData<T> Unique(Func<T, T, bool> Comparer)
        { 
            SqlData<T> ret = new SqlData<T>();
            foreach(T t in this)
            {
                bool y = false;
                foreach (T a in this)
                {
                    if (Comparer(t, a))
                        y = true;
                }
                if (!y)
                    ret.Add(t);
            }
            return ret;
        }
        /// <summary>
        /// Gets first item from current data holder.
        /// </summary>
        /// <returns>First item from current data holder.</returns>
        public T First()
        {
            return this[0];
        }
        /// <summary>
        /// Gets last item from current data holder.
        /// </summary>
        /// <returns>Last item from current data holder.</returns>
        public T Last()
        {
            return this[this.Count - 1];
        }
    }
}
